//bCal Javascript Calendar Generator
//author: Baburaj P James
var bCal = function (params) {
    var self = this;

    //private variables
    var calendarTableContainer;
    var today = new moment();
    var leftArrow;
    var rightArrow;
    var calendarTitle;
    var baseYear;
    var yearsRange;

    //options
    var leftArrowImgSrc = 'https://cdn1.iconfinder.com/data/icons/cc_mono_icon_set/blacks/16x16/sq_br_prev.png';
    var rightArrowImgSrc = 'https://cdn1.iconfinder.com/data/icons/cc_mono_icon_set/blacks/16x16/sq_br_next.png';
    var weekDays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
    var monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
    var baseDate = new moment(today.year() + '-' + (today.month() + 1) + '-' + today.date(), 'YYYY-MM-DD');
    var weekStartsOn = 1; //indexed with Sunday being 0
    var elementSelector = 'body';
    var viewMode = 'years';
    var dayClickMethod = function (date) { alert(date); };

    //private methods
    var getCalendarBeginDate = function () {
        var thisMonthBeginning = new moment(baseDate.year() + '-' + (baseDate.month() + 1) + '-1', 'YYYY-M-D');
        var daysBefore = thisMonthBeginning.day() - weekStartsOn >= 0 ? -1 * (weekStartsOn - thisMonthBeginning.day()) : 7 - (weekStartsOn - thisMonthBeginning.day());
        return thisMonthBeginning.clone().add('days', -1 * daysBefore);
    };
    var getCurrentMonthEnd = function () {
        return new moment(baseDate.year() + '-' + (baseDate.month() + 1) + '-' + baseDate.daysInMonth(), 'YYYY-M-D');
    };
    var getCalendarTable = function () {
        var table = $('<table class="bCal-base"/>');
        var tableHead = $('<thead/>');
        var tableHeadRow = $('<tr/>');
        for (var i = 0; i < weekDays.length; i++) {
            var th = $('<th/>');
            th.html(weekDays[(weekStartsOn + i) % 7]);
            tableHeadRow.append(th);
        }
        var tableBody = $('<tbody/>');
        var start = getCalendarBeginDate();
        var end = getCurrentMonthEnd();
        var rowCount = 0;
        while (rowCount++ < 6) {
            var tableBodyRow = $('<tr/>');
            for (var i = 0; i < 7; i++) {
                var td = $('<td/>');
                td.html(start.date());
                var hidden = $('<input type="hidden" value="' + start.format('YYYY-MM-DD') + '"/>');
                td.append(hidden);
                td.on('click', dayClick);
                tableBodyRow.append(td);
                if (start.month() != baseDate.month()) td.css('visibility', 'hidden');
                if (start.format('YYYY-MM-DD') == today.format('YYYY-MM-DD')) td.addClass('bCal-today');
                start.add('days', 1);
            }
            tableBody.append(tableBodyRow);
        }
        table.append(tableHead.append(tableHeadRow)).append(tableBody);
        return table;
    };
    var getYearTable = function () {
        var table = $('<table class="bCal-base" style="width:100%;height:100%"/>');
        var tableBody = $('<tbody/>');
        var rowCount = 0;
        var cellCount = 0;
        while (rowCount++ < 4) {
            var tableBodyRow = $('<tr/>');
            for (var i = 0; i < 3; i++) {
                var td = $('<td style="height:25%;width:33.33%"/>');
                var hidden = $('<input type="hidden" value="' + baseYear.toString() + '-' + (cellCount + 1).toString() + '-1' + '"/>');
                if (today.month() == cellCount && today.year() == baseYear) td.addClass('bCal-today');
                td.html(monthNames[cellCount++]);
                td.append(hidden);
                td.on('click', monthClick);
                tableBodyRow.append(td);
            }
            tableBody.append(tableBodyRow);
        }
        table.append(tableBody);
        return table;
    };
    var getYearsTable = function () {
        var table = $('<table class="bCal-base" style="width:100%;height:100%"/>');
        var tableBody = $('<tbody/>');
        var rowCount = 0;
        var cellCount = 0;
        var yearStart = baseYear - 7;
        var yearEnd = baseYear + 7;
        yearsRange = [yearStart, yearEnd];
        while (rowCount++ < 5) {
            var tableBodyRow = $('<tr/>');
            for (var i = 0; i < 3; i++) {
                var td = $('<td style="height:20%;width:33.33%"/>');
                if (today.year() == yearStart) td.addClass('bCal-today');
                td.html((yearStart++).toString());
                td.on('click', yearClick);
                tableBodyRow.append(td);
            }
            tableBody.append(tableBodyRow);
        }
        table.append(tableBody);
        return table;
    };
    var getCalendarWrapTable = function () {
        var table = $('<table class="bCal-main"/>');
        table.addClass('bCal');

        var header = $('<tr/>');
        var headerTd = $('<td/>');
        header.append(headerTd);
        table.append(header);
        header = headerTd;

        var content = $('<tr/>');
        var contentTd = $('<td/>');
        content.append(contentTd);
        table.append(content);
        content = contentTd;

        var headerContent = $('<div class="bCal-header"/>');
        var leftArrowDiv = $('<div/>');
        leftArrowDiv.css('float', 'left');
        var leftArrowImg = $('<img/>');
        var titleTextSpan = $('<span/>');
        calendarTitle = titleTextSpan;
        var rightArrowDiv = $('<div/>');
        rightArrowDiv.css('float', 'right');
        var rightArrowImg = $('<img/>');
        leftArrowImg.attr('src', leftArrowImgSrc);
        leftArrowDiv.append(leftArrowImg);
        rightArrowImg.attr('src', rightArrowImgSrc);
        rightArrowDiv.append(rightArrowImg);
        headerContent.append(leftArrowDiv).append(titleTextSpan).append(rightArrowDiv);
        header.append(headerContent);
        calendarTableContainer = content;

        leftArrowImg.on('click', leftArrowClick);
        leftArrow = leftArrowImg;
        rightArrowImg.on('click', rightArrowClick);
        rightArrow = rightArrowImg;
        titleTextSpan.on('click', titleClick);

        return table;
    }
    var overrideDefaults = function () {
        if ($.type(params) == 'null' || $.type(params) == 'undefined') return;
        if ($.type(params) == 'string') elementSelector = params;
        else {
            if (params.LeftArrowImageSource) leftArrowImgSrc = params.LeftArrowImageSource;
            if (params.RightArrowImageSource) rightArrowImgSrc = params.RightArrowImageSource;
            if (params.BaseDate) baseDate = new moment(params.BaseDate);
            if (params.WeekStartsOn) weekStartsOn = new moment(params.WeekStartsOn);
            if (params.ParentElement) elementSelector = params.ParentElement;
            if (params.CustomWeekDayTitles) weekDays = params.CustomWeekDayTitles;
            if (params.ClickFunction) dayClickMethod = params.ClickFunction;
        }
    };
    var displayContent = function (content, title) {
        calendarTableContainer.children().fadeOut(100);
        calendarTableContainer.html(content.css('display', 'none').fadeIn(100));
        calendarTitle.html(title);
    };

    //event handlers
    var leftArrowClick = function (e) {
        if (viewMode == 'calendar') {
            baseDate = baseDate.subtract('months', 1);
            baseYear = baseDate.year();
            displayContent(getCalendarTable(), baseDate.format('MMMM YYYY'));
        }
        if (viewMode == 'year') {
            baseYear--;
            displayContent(getYearTable(), baseYear.toString());
        }
        if (viewMode == 'years') {
            baseYear -= 15;
            displayContent(getYearsTable(), yearsRange[0].toString() + ' - ' + yearsRange[1].toString());
        }
    };
    var rightArrowClick = function (e) {
        if (viewMode == 'calendar') {
            baseDate = baseDate.add('months', 1);
            baseYear = baseDate.year();
            displayContent(getCalendarTable(), baseDate.format('MMMM YYYY'));
        }
        if (viewMode == 'year') {
            baseYear++;
            displayContent(getYearTable(), baseYear.toString());
        }
        if (viewMode == 'years') {
            baseYear += 15;
            displayContent(getYearsTable(), yearsRange[0].toString() + ' - ' + yearsRange[1].toString());
        }
    };
    var titleClick = function (e) {
        switch (viewMode) {
            case 'calendar':
                viewMode = 'year';
                displayContent(getYearTable(), baseYear.toString());
                break;
            case 'year':
                viewMode = 'years';
                displayContent(getYearsTable(), yearsRange[0].toString() + ' - ' + yearsRange[1].toString());
                break;
            case 'years':
                viewMode = 'calendar';
                displayContent(getCalendarTable(), baseDate.format('MMMM YYYY'));
                break;
        }
    };
    var monthClick = function (e) {
        var value = $(this).find('input').val();
        var valueDate = new moment(value, 'YYYY-MM-DD');
        baseDate = valueDate;
        viewMode = 'years';
        titleClick();
    };
    var yearClick = function (e) {
        var valueYear = $(this).html();
        baseYear = valueYear;
        viewMode = 'calendar';
        titleClick();
    };
    var dayClick = function (e) {
        var valueDate = $(this).find('input').val();
        var momentObj = new moment(valueDate, 'YYYY-MM-DD');
        dayClickMethod(momentObj)
    };

    //constructor
    self.bCal = $(function () {
        //read from params
        overrideDefaults();
        baseYear = baseDate.year();
        var table = getCalendarWrapTable();
        titleClick();
        $(elementSelector).append(table);
        calendarTableContainer.css('width', calendarTableContainer.width() + 'px');
        calendarTableContainer.css('height', calendarTableContainer.height() + 'px');
        return self;
    });

    //public methods
    self.ShowCurrentMonth = function () {
        baseDate = today;
        viewMode = 'years';
        titleClick();
        return self;
    }
    self.GotoDate = function (momentObj) {
        baseDate = momentObj;
        viewMode = 'years';
        titleClick();
        return self;
    };
};